package log

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"sync"
	"time"

	"gitee.com/doraemon1996/bunny/utils"
)

var (
	logPackage     string // 日志包的包名
	callerInitOnce sync.Once
)

const (
	maxDepth int = 20 // 最大调用栈深度
	minDepth int = 2  // 最小调用栈深度
)

// 日志数据条目结构
type Entry struct {
	// 日志对象
	Logger *Logger

	// 自定义键值对
	Data Fields

	// 日志条目创建时间
	Time time.Time

	// 日志等级
	Level Level

	// 调用栈
	Caller *runtime.Frame

	// 日志数据
	Message string

	// 用户设置的上下文,用于hook中使用
	Context context.Context

	// 日志条目处理错误
	err string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		Data:   make(Fields),
	}
}

func (entry *Entry) Dup() *Entry {
	data := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}

	return &Entry{
		Logger:  entry.Logger,
		Data:    data,
		Time:    entry.Time,
		Context: entry.Context,
		err:     entry.err,
	}
}

// 返回该日志条目的字节形式
func (entry *Entry) Bytes() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := entry.Logger.Formatter.Format(entry, buf)
	return buf.Bytes(), err
}

// 返回该日志条目的字符串形式
func (entry *Entry) String() (string, error) {
	buf := new(bytes.Buffer)
	err := entry.Logger.Formatter.Format(entry, buf)
	return buf.String(), err
}

func (entry *Entry) WithField(key string, value any) *Entry {
	return entry.WithFields(Fields{key: value})
}

func (entry *Entry) WithFields(fields Fields) *Entry {
	data := make(Fields, len(entry.Data)+len(fields))
	for k, v := range entry.Data {
		data[k] = v
	}
	fieldErr := entry.err
	for k, v := range fields {
		isFunc := false
		if t := reflect.TypeOf(v); t != nil {
			if t.Kind() == reflect.Func ||
				(t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func) {
				isFunc = true
			}
		}
		if isFunc {
			if fieldErr != "" {
				fieldErr = fieldErr + "," + fmt.Sprintf("unsupport field %q", k)
			} else {
				fieldErr = fmt.Sprintf("unsupport field %q", k)
			}
		} else {
			data[k] = v
		}
	}

	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: fieldErr}
}

func (entry *Entry) WithTime(t time.Time) *Entry {
	data := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}

	return &Entry{Logger: entry.Logger, Data: data, Time: t, Context: entry.Context, err: entry.err}
}

func (entry *Entry) WithContext(ctx context.Context) *Entry {
	data := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}

	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: ctx, err: entry.err}
}

func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(LogKey_Error, err)
}

// 循环查找第一个非日志包调用函数
func getCaller() *runtime.Frame {
	callerInitOnce.Do(func() {
		pc, _, _, _ := runtime.Caller(0)
		funcName := runtime.FuncForPC(pc).Name()
		logPackage = utils.PackageFromFunction(funcName)
	})

	pcs := make([]uintptr, maxDepth)
	depth := runtime.Callers(minDepth, pcs)
	frames := runtime.CallersFrames(pcs[:depth])
	for frame, more := frames.Next(); more; frame, more = frames.Next() {
		if utils.PackageFromFunction(frame.Function) != logPackage {
			return &frame
		}
	}

	return nil
}

// 将序列化后的数据放进写日志通道
func (entry *Entry) write(formatter Formatter) {
	buffer := bufPool.Get()
	buffer.Reset()

	if err := formatter.Format(entry, buffer); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to format log entry. error: %v\n", err)
		return
	}

	entry.Logger.writer.Append(buffer)
}

func (entry *Entry) log(level Level, msg string) {
	entry.Logger.mutex.RLock()
	formatter := entry.Logger.Formatter
	reportCaller := entry.Logger.ReportCaller
	hooks := make(LevelHooks, len(entry.Logger.Hooks))
	for k, v := range entry.Logger.Hooks {
		hooks[k] = v
	}
	entry.Logger.mutex.RUnlock()

	newEntry := entry.Dup()
	if newEntry.Time.IsZero() {
		newEntry.Time = time.Now()
	}
	newEntry.Level = level
	newEntry.Message = msg
	if reportCaller {
		newEntry.Caller = getCaller()
	}

	hooks.Fire(newEntry.Level, newEntry)
	newEntry.write(formatter)

	if level <= Level_Panic {
		panic(newEntry)
	}
}

func (entry *Entry) HasCaller() bool {
	return entry.Logger != nil &&
		entry.Logger.ReportCaller &&
		entry.Caller != nil
}

// Sprit类型日志函数

// 对于Level_Fatal日志应该用Entry.Fatal代替
func (entry *Entry) Log(level Level, args ...any) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.log(level, fmt.Sprint(args...))
	}
}

func (entry *Entry) Trace(args ...any) {
	entry.Log(Level_Trace, args...)
}

func (entry *Entry) Debug(args ...any) {
	entry.Log(Level_Debug, args...)
}

func (entry *Entry) Print(args ...any) {
	entry.Log(Level_Info, args...)
}

func (entry *Entry) Info(args ...any) {
	entry.Log(Level_Info, args...)
}

func (entry *Entry) Warn(args ...any) {
	entry.Log(Level_Warn, args...)
}

func (entry *Entry) Warning(args ...any) {
	entry.Log(Level_Warn, args...)
}

func (entry *Entry) Error(args ...any) {
	entry.Log(Level_Error, args...)
}

func (entry *Entry) Fatal(args ...any) {
	entry.Log(Level_Fatal, args...)
	Exit(1)
}

func (entry *Entry) Panic(args ...any) {
	entry.Log(Level_Panic, args...)
}

// Sprintf类型日志函数

// 对于Level_Fatal日志应该用Entry.Fatalf代替
func (entry *Entry) Logf(level Level, format string, args ...any) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.log(level, fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Tracef(format string, args ...any) {
	entry.Logf(Level_Trace, format, args...)
}

func (entry *Entry) Debugf(format string, args ...any) {
	entry.Logf(Level_Debug, format, args...)
}

func (entry *Entry) Printf(format string, args ...any) {
	entry.Logf(Level_Info, format, args...)
}

func (entry *Entry) Infof(format string, args ...any) {
	entry.Logf(Level_Info, format, args...)
}

func (entry *Entry) Warnf(format string, args ...any) {
	entry.Logf(Level_Warn, format, args...)
}

func (entry *Entry) Warningf(format string, args ...any) {
	entry.Logf(Level_Warn, format, args...)
}

func (entry *Entry) Errorf(format string, args ...any) {
	entry.Logf(Level_Error, format, args...)
}

func (entry *Entry) Fatalf(format string, args ...any) {
	entry.Logf(Level_Fatal, format, args...)
	Exit(1)
}

func (entry *Entry) Panicf(format string, args ...any) {
	entry.Logf(Level_Panic, format, args...)
}

// Sprintln类型日志函数

// 对于Level_Fatal日志应该用Entry.Fatalln代替
func (entry *Entry) Logln(level Level, args ...any) {
	if entry.Logger.IsLevelEnabled(level) {
		msg := fmt.Sprintln(args...)
		entry.log(level, string(msg[:len(msg)-1]))
	}
}

func (entry *Entry) Traceln(args ...any) {
	entry.Logln(Level_Trace, args...)
}

func (entry *Entry) Debugln(args ...any) {
	entry.Logln(Level_Debug, args...)
}

func (entry *Entry) Println(args ...any) {
	entry.Logln(Level_Info, args...)
}

func (entry *Entry) Infoln(args ...any) {
	entry.Logln(Level_Info, args...)
}

func (entry *Entry) Warnln(args ...any) {
	entry.Logln(Level_Warn, args...)
}

func (entry *Entry) Warningln(args ...any) {
	entry.Logln(Level_Warn, args...)
}

func (entry *Entry) Errorln(args ...any) {
	entry.Logln(Level_Error, args...)
}

func (entry *Entry) Fatalln(args ...any) {
	entry.Logln(Level_Fatal, args...)
	Exit(1)
}

func (entry *Entry) Panicln(args ...any) {
	entry.Logln(Level_Panic, args...)
}
